<?php

namespace xcxApi\behaviors;

use Carbon\Carbon;
use common\components\fast_api\ApiException;
use common\components\wtools\tools\ArrayHelper;
use common\components\wtools\tools\Security;
use common\datas\Enums;
use common\Tools;
use xcxApi\models\db\MpUsers;
use yii\base\ActionFilter;

class JwtTokenCheck extends ActionFilter
{
    public $reLoginStatusCode = 5601;

    public function init()
    {
        parent::init();
        $this->reLoginStatusCode = \Yii::$app->params['jwt']['xcxApi']['reLoginStatusCode'];
    }

    /**
     * @param \yii\base\Action $action
     * @return bool
     * @throws
     */
    public function beforeAction($action)
    {
        $request = \Yii::$app->request;
        $params = $request->post();
        if (isset($params['token'])) {
            $this->_throwError(201808161402, 'token请在header里请求，否则会有安全隐患');
        }
        if (isset($params['key'])) {
            $this->_throwError(201808161401, '请不要传key，会有安全隐患');
        }
        try {
            $jwtToken = $request->headers->get('authorization');
            $jwtToken = str_replace("Bearer ", "", $jwtToken);
            if (!$jwtToken) {
                $this->_throwError(201801010000, 'jwtToken未找到');
            }
            $jwtTokenArr['token'] = $jwtToken;
            $tokenArr = MpUsers::getJwtToken($jwtTokenArr);
            $mpUser = $tokenArr['mpUser'];
            $fMpUser = MpUsers::getFilterShow($mpUser);
            if (!$fMpUser) {
                $this->_throwError(201808161455, "没有找到用户,请重新登陆");
            }
        } catch (\Exception $e) {
            $this->_throwError($e->getCode(), $e->getMessage(), $this->reLoginStatusCode);
        }
        if (\Yii::$app->params['isCheckTime']) {
            if (!isset($params['timestamp'])) {
                $this->_throwError(201808161406, '请求必须有当前操作时间戳');
            }
            if (strlen($params['timestamp']) != 10) {
                $this->_throwError(201808161407, '时间戳长度必须为10位秒数');
            }
            if (abs(substr($params['timestamp'], 0, 10) - Carbon::now()->timestamp) > 600){
                $this->_throwError(201808161408, '操作超时或时间异常，请检查设备时间');
            }
        }
        if (\Yii::$app->params['isCheckNonce']) {
            if (!isset($params['nonce'])) {
                $this->_throwError(201808161409, '请求必须设置随机数');
            }
            if (strlen($params['nonce']) < 9 || strlen($params['nonce']) > 181) {
                $this->_throwError(201808161411, '随机数长度错误');
            }
            $nonce_key = APP_ID.'/nonceUser/'.$mpUser->id;
            $tmp_nonces = is_array(\Yii::$app->cache->get($nonce_key))?\Yii::$app->cache->get($nonce_key):[];
            if (!in_array($params['nonce'], $tmp_nonces)){
                $tmp_nonces[] = $params['nonce'];
                if (count($tmp_nonces) > 10000) {
                    array_shift($tmp_nonces);
                }
                \Yii::$app->cache->set($nonce_key, $tmp_nonces, 3600 * 25);
            }else{
                $this->_throwError(201808161412, '请求随机数重复');
            }
        }
        if (\Yii::$app->params['isCheckSign']) {
            if (!isset($params['sign'])) {
                $this->_throwError(201808161404, '请求必须有请求签名');
            }
            if (strlen($params['sign']) !== 32) {
                $this->_throwError(201808161405, '请求签名长度错误');
            }
            $p = $params;
            $tokenMd5 = md5($tokenArr['jwt']['payload']['token']);
            $p[$tokenMd5] = md5($mpUser->key.$tokenArr['jwt']['payload']['endAt']);
            ksort($p);
            unset($p['sign']);
            $p_str = Tools::toQuery($p);
            $_sign = md5($p_str);
            if ($_sign != $params['sign']){
                $this->_throwError(201808161457, "验签失败", $this->reLoginStatusCode);
            }
        }
        if ($mpUser->status != Enums::ACTIVE){
            $this->_throwError(201808161456, "用户状态异常，如果你被禁了，请联系管理员");
        }
        \Yii::$app->user->login($mpUser);
        return parent::beforeAction($action);
    }

    /**
     * @param $code
     * @param $msg
     * @param int $statusCode
     * @return mixed
     * @throws ApiException
     */
    private function _throwError($code, $msg, $statusCode = 500)
    {
        \Yii::$app->response->headers->set('status', $statusCode);
        throw new ApiException($code, $msg);
    }
}
